#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
//浅拷贝讨论：
//定义一个指针数据成员，执行默认的浅拷贝操作
//之前写过一个案例，写时复制的案例，也是执行的是浅拷贝，只有在写的时候
//才会去执行深拷贝
//为什么我们写的指针数据成员的案例会出现double free，但是写时复制没有出现
class String{
public:
    //构造函数的时候，执行的是深拷贝
    String(const char * pstr)
    :_pstr(new char[strlen(pstr) + 1]())
    {
        strcpy(_pstr, pstr);
    }

    ~String(){
        if(_pstr){
            delete [] _pstr;
        }
    }

private:
    char * _pstr;
};
//写时复制的思想：
//需要有一个引用计数来标识当前某一个堆区域被多少个对象所引用
//当所有的引用断开之后，需要回收堆空间
//引用计数和字符串存储在一起的，前面分配了4字节空间
class CowString{
public:
    //执行的是深拷贝，申请空间的时候多申请了4字节
    //并且指针指向的位置也是后面
    CowString(const char * pstr)
    :_pstr(new char[strlen(pstr) + 1 + 4]() + 4)
    {
        strcpy(_pstr, pstr);
    }
    //此时执行的是浅拷贝操作
    //编译器给我们提供的就是当前的这种写法
    //但是我们还需要额外再去做一些处理
    CowString(const CowString & rhs)
    :_pstr(rhs._pstr)
    {
        //引用计数需要增加
        ++*(int *)(_pstr - 4);
    }
    //析构函数也不一样
    ~CowString(){
        if(--*(int *)(_pstr - 4) == 0){
            delete [] (_pstr - 4);
            _pstr = nullptr;
        }
    }

private:
    char * _pstr;
};


void test(){
    String s("hello");
    //因为这个地方没有编写拷贝构造函数，用的是编译器默认提供的
    //所以默认就是浅拷贝操作
    String s2 = s;

}

int main()
{
    test();
    return 0;
}

